<template>
  <view class="slider" :style="{ height: barHeight + 'px' }" @click="onTrackClick">

    <view class="slider-track" :style="{ backgroundColor: inactiveColor }"></view>

    <view
        class="slider-bar"
        :style="{
        left: barLeft + '%',
        width: barWidth + '%',
        backgroundColor: activeColor
      }"
    ></view>

    <view
        v-for="(pos, index) in buttonPositions"
        :key="index"
        class="slider-button"
        :style="{ left: pos + '%' }"
        @touchstart.stop="onTouchStart($event, index)"
        @touchmove.stop.prevent="onTouchMove($event)"
        @touchend.stop="onTouchEnd"
        @mousedown.stop="onMouseDown($event, index)"
    >
      <view class="slider-thumb" :style="{ backgroundColor: buttonColor }">
        <view class="circle-center radius"  :style="{ backgroundColor: centerCircleColor }"></view>
      </view>

      <view class="slider-label">
        <slot name="button" :index="index">
          {{ displayValue(index) }}
        </slot>
      </view>
    </view>
  </view>
</template>

<script>
export default {
  name: "MySlider",
  props: {
    value: { type: [Number, Array], default: 0 },
    min: { type: Number, default: 0 },
    max: { type: Number, default: 100 },
    step: { type: Number, default: 1 },
    disabled: { type: Boolean, default: false },
    barHeight: { type: Number, default: 4 },
    activeColor: { type: String, default: "rgb(28, 128, 10)" },
    inactiveColor: { type: String, default: "#e5e5e5" },
    buttonColor: { type: String, default: "#fff" },
    centerCircleColor: { type: String, default: "rgb(28, 128, 10)" },
    range: { type: Boolean, default: false }
  },
  data() {
    return {
      dragging: false,
      activeButton: 0,
      startX: 0,
      startValue: 0,
      currentValue: this.initValue()
    };
  },
  computed: {
    rangeDiff() {
      return this.max - this.min;
    },
    rangeValue() {
      return this.range
          ? [...this.currentValue].sort((a, b) => a - b)
          : [this.currentValue];
    },
    buttonPositions() {
      return this.rangeValue.map(v => ((v - this.min) / this.rangeDiff) * 100);
    },
    barLeft() {
      return this.range ? this.buttonPositions[0] : 0;
    },
    barWidth() {
      return this.range
          ? this.buttonPositions[1] - this.buttonPositions[0]
          : this.buttonPositions[0];
    }
  },
  watch: {
    value(val) {
      this.currentValue = this.initValue(val);
    }
  },
  methods: {
    initValue(val = this.value) {
      if (this.range) {
        if (Array.isArray(val) && val.length === 2) return val;
        return [this.min, this.max];
      }
      return typeof val === "number" ? val : this.min;
    },
    format(value) {
      const stepValue =
          Math.round((value - this.min) / this.step) * this.step + this.min;
      return Math.min(this.max, Math.max(this.min, stepValue));
    },
    updateValue(rawValue, emitChange = false, formatStep = true) {
      let newVal;
      if (this.range) {
        newVal = [...this.currentValue];
        let val = formatStep ? this.format(rawValue) : rawValue;
        newVal[this.activeButton] = val;
        if (this.activeButton === 0 && newVal[0] > newVal[1]) {
          this.activeButton = 1;
        } else if (this.activeButton === 1 && newVal[1] < newVal[0]) {
          this.activeButton = 0;
        }
        this.currentValue = newVal;
        this.$emit("input", [...newVal], this.activeButton);
        if (emitChange) this.$emit("change", [...newVal]);
      } else {
        let val = formatStep ? this.format(rawValue) : rawValue;
        this.currentValue = val;
        this.$emit("input", val);
        if (emitChange) this.$emit("change", val);
      }
    },
    displayValue(index) {
      return this.range ? this.rangeValue[index] : this.currentValue;
    },
    onTrackClick(e) {
      if (this.disabled || this.dragging) return;
      const rect = e.currentTarget.getBoundingClientRect();
      const percent = (e.clientX - rect.left) / rect.width;
      const newValue = this.min + percent * this.rangeDiff;

      if (this.range) {
        const dist0 = Math.abs(newValue - this.rangeValue[0]);
        const dist1 = Math.abs(newValue - this.rangeValue[1]);
        this.activeButton = dist0 <= dist1 ? 0 : 1;
      }
      this.updateValue(newValue, true, true);
    },
    onTouchStart(e, index) {
      if (this.disabled) return;
      this.dragging = true;
      this.activeButton = index || 0;
      this.startX = e.touches[0].clientX;
      this.startValue = this.range
          ? this.currentValue[this.activeButton]
          : this.currentValue;
      this.$emit("drag-start", this.activeButton);
    },
    onTouchMove(e) {
      if (!this.dragging) return;
      const deltaX = e.touches[0].clientX - this.startX;
      const track = this.$el.getBoundingClientRect();
      const diffValue = (deltaX / track.width) * this.rangeDiff;
      this.updateValue(this.startValue + diffValue);
    },
    onTouchEnd() {
      if (!this.dragging) return;
      this.dragging = false;
      this.$emit("drag-end", this.activeButton, this.currentValue);
      if (this.range) {
        this.updateValue(this.currentValue[this.activeButton], true, true);
      } else {
        this.updateValue(this.currentValue, true, true);
      }
    },
    onMouseDown(e, index) {
      if (this.disabled) return;
      this.dragging = true;
      this.activeButton = index || 0;
      this.startX = e.clientX;
      this.startValue = this.range
          ? this.currentValue[this.activeButton]
          : this.currentValue;
      this.$emit("drag-start", this.activeButton);

      const onMouseMove = moveEvent => {
        const deltaX = moveEvent.clientX - this.startX;
        const track = this.$el.getBoundingClientRect();
        const diffValue = (deltaX / track.width) * this.rangeDiff;
        this.updateValue(this.startValue + diffValue);
      };

      const onMouseUp = () => {
        this.dragging = false;
        this.$emit("drag-end", this.activeButton, this.currentValue);
        if (this.range) {
          this.updateValue(this.currentValue[this.activeButton], true, true);
        } else {
          this.updateValue(this.currentValue, true, true);
        }
        document.removeEventListener("mousemove", onMouseMove);
        document.removeEventListener("mouseup", onMouseUp);
      };

      document.addEventListener("mousemove", onMouseMove);
      document.addEventListener("mouseup", onMouseUp);
    }
  }
};
</script>

<style scoped>
.slider {
  position: relative;
  width: 100%;
  height: 40rpx;
}
.slider-track {
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  height: 10rpx;
  background-color: #ebedf0;
  border-radius: 4rpx;
  transform: translateY(-50%);
}
.slider-bar {
  position: absolute;
  top: 50%;
  height: 10rpx;
  background-color: #1989fa;
  border-radius: 4rpx;
  transform: translateY(-50%);
}
.slider-button {
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  touch-action: none;
}

.slider-thumb {
  width: 20rpx;
  height: 20rpx;
  border-radius: 50%;
  box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
  padding: 16rpx;
  .circle-center {
    height: 100%;
    width: 100%;
  }
}

.slider-label {
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  margin-top: 8rpx;
  font-size: 24rpx;
  color: #333;
  white-space: nowrap;
}
</style>
